if exists("s:loaded")
    finish
endif
let s:loaded = 1

let g:coc_config_home = $VIM."/vimfiles/coc/config"
if has('win32')
    let g:coc_data_home = $VIM."/vimfiles/coc/data"
elseif has('unix')
    let g:coc_data_home = $HOME."/.local/share/coc_data"
endif
let g:coc_global_extensions = [
            \ 'coc-calc',
            \ 'coc-cmake',
            \ 'coc-floaterm',
            \ 'coc-json',
            \ 'coc-lists',
            \ 'coc-marketplace',
            \ 'coc-omnisharp',
            \ 'coc-powershell',
            \ 'coc-pyright',
            \ 'coc-rust-analyzer',
            \ 'coc-snippets',
            \ 'coc-sql',
            \ 'coc-tasks',
            \ 'coc-vimlsp'
            \ ]

" 通用
nmap <Leader>jd <Plug>(coc-definition)

" coc-calc
    " append result on current expression
    nmap <Leader>cca <Plug>(coc-calc-result-append)
    " replace result on current expression
    nmap <Leader>ccr <Plug>(coc-calc-result-replace)

" coc-cmake
    nmap <Leader>ccm :<c-u>CocCommand cmake.onlineHelp<cr>

" coc-floaterm
    let g:floaterm_autoclose = 1
" coc-json
    " Retry resolve schema of current buffer
    nmap <Leader>cj :<c-u>CocCommand json.retryResolveSchema<cr>

" coc-lists
" coc-marketplace
" coc-omnisharp
" coc-powershell

" coc-pyright
    " Run linting
    nmap <Leader>cpl :<c-u>CocCommand python.runLinting<cr>
    " Sort imports by isort
    nmap <Leader>cps :<c-u>CocCommand python.sortImports<cr>
    " Show the currently used Pyright version in :messages
    nmap <Leader>cpv :<c-u>CocCommand pyright.version<cr>
    " Organize imports by Pyright
    nmap <Leader>cpo :<c-u>CocCommand pyright.organizeimports<cr>
    " This command forces the type checker to discard all of its cached type
    " information and restart analysis. It is useful in cases where new type
    " stubs or libraries have been installed.
    nmap <Leader>cpo :<c-u>CocCommand pyright.restartserver<cr>
    " Creates Type Stubs with given module name, for example :CocCommand
    " pyright.createtypestub numpy
    nmap <Leader>cpc :<c-u>CocCommand pyright.createtypestub<cr>

" coc-rust-analyzer
    " Show rust-analyzer status
    nmap <Leader>crrs :<c-u>CocCommand rust-analyzer.analyzerStatus<cr>
    " Expand macro recursively
    nmap <Leader>crem :<c-u>CocCommand rust-analyzer.expandMacro<cr>
    " Explain the currently hovered error message
    nmap <Leader>cree :<c-u>CocCommand rust-analyzer.explainError<cr>
    " Join lines
    nmap <Leader>crj :<c-u>CocCommand rust-analyzer.joinLines<cr>
    " Find matching brace
    nmap <Leader>crb :<c-u>CocCommand rust-analyzer.matchingBrace<cr>
    " Memory Usage (Clears Database)
    nmap <Leader>crmu :<c-u>CocCommand rust-analyzer.memoryUsage<cr>
    " Move item up
    nmap <Leader>criu :<c-u>CocCommand rust-analyzer.moveItemUp<cr>
    " Move item down
    nmap <Leader>crid :<c-u>CocCommand rust-analyzer.moveItemDown<cr>
    " Open docs under cursor
    nmap <Leader>crd :<c-u>CocCommand rust-analyzer.openDocs<cr>
    " Locate parent module
    nmap <Leader>crpm :<c-u>CocCommand rust-analyzer.parentModule<cr>
    " Peek related tests
    nmap <Leader>crpt :<c-u>CocCommand rust-analyzer.peekTests<cr>
    " Restart rust-analyzer server
    nmap <Leader>crrd :<c-u>CocCommand rust-analyzer.reload<cr>
    " Reload workspace
    nmap <Leader>crrw :<c-u>CocCommand rust-analyzer.reloadWorkspace<cr>
    " List available runnables of current file
    nmap <Leader>crru :<c-u>CocCommand rust-analyzer.run<cr>
    " Show current Rust Analyzer server version
    nmap <Leader>crsv :<c-u>CocCommand rust-analyzer.serverVersion<cr>
    " Structural Search Replace
    nmap <Leader>crsr :<c-u>CocCommand rust-analyzer.ssr<cr>
    " Show syntax tree
    nmap <Leader>crst :<c-u>CocCommand rust-analyzer.syntaxTree<cr>
    " Toggle inlay hints on/off
    nmap <Leader>crt :<c-u>CocCommand rust-analyzer.toggleInlayHints<cr>
    " Download latest rust-analyzer from GitHub release
    nmap <Leader>cru :<c-u>CocCommand rust-analyzer.upgrade<cr>
    " View Hir
    nmap <Leader>crv :<c-u>CocCommand rust-analyzer.viewHir<cr>

" coc-snippets
    " open snippets list
    nmap <Leader>csl :<c-u>CocList snippets<cr>
    " edit user snippet of current filetype
    nmap <Leader>cse :<c-u>CocCommand snippets.editSnippets<cr>
    " open snippet files of current filetype.
    nmap <Leader>cso :<c-u>CocCommand snippets.openSnippetFiles<cr>

    " Use <C-l> for trigger snippet expand.
    imap <C-l> <Plug>(coc-snippets-expand)
    " Use <C-j> for select text for visual placeholder of snippet.
    vmap <C-j> <Plug>(coc-snippets-select)
    " Use <C-j> for jump to next placeholder, it's default of coc.nvim
    " let g:coc_snippet_next = '<c-j>'
    " Use <C-k> for jump to previous placeholder, it's default of coc.nvim
    let g:coc_snippet_prev = '<c-k>'
    " Use <C-j> for both expand and jump (make expand higher priority.)
    imap <C-j> <Plug>(coc-snippets-expand-jump)
    " Use <leader>x for convert visual selected code to snippet
    xmap <leader>x  <Plug>(coc-convert-snippet)

    inoremap <silent><expr> <TAB>
                \ pumvisible() ? coc#_select_confirm() :
                \ coc#expandableOrJumpable() ? "\<C-r>=coc#rpc#request('doKeymap', ['snippets-expand-jump',''])\<CR>" :
                \ <SID>check_back_space() ? "\<TAB>" :
                \ coc#refresh()

    function! s:check_back_space() abort
        let col = col('.') - 1
        return !col || getline('.')[col - 1]  =~# '\s'
    endfunction

    let g:coc_snippet_next = '<tab>'

" coc-sql
    " format for current file
    nmap <Leader>csq :<c-u>CocCommand sql.Format<cr>

" coc-tasks
" coc-vimlsp
    let g:markdown_fenced_languages = [
                \ 'vim',
                \ 'help'
                \]

" 使用 <tab> 触发补全
function! s:check_back_space() abort
    let col = col('.') - 1
    return !col || getline('.')[col - 1]  =~ '\s'
endfunction

inoremap <silent><expr> <TAB>
            \ pumvisible() ? "\<C-n>" :
            \ <SID>check_back_space() ? "\<TAB>" :
            \ coc#refresh()

" 使用 <c-space> 强制触发补全
inoremap <silent><expr> <c-space> coc#refresh()

" 使用 `<cr>` 确认补全
inoremap <expr> <cr> pumvisible() ? "\<C-y>" : "\<CR>"

" 使用 <CR> 确认补全，并触发 coc.nvim 的 `formatOnType` 功能
inoremap <silent><expr> <cr> pumvisible() ? coc#_select_confirm()
            \: "\<C-g>u\<CR>\<c-r>=coc#on_enter()\<CR>"

let s:win32exe_path = $VIM.'\vimfiles\win32exe'
let s:ccls_path = s:win32exe_path . '\ccls'
let s:lua_language_server_path = s:win32exe_path . '\lua-language-server'
if has('win32')
    let s:session_path = "D:/Temp/sessions"
elseif has('unix')
    let s:session_path = "/tmp/sessions"
endif

call coc#config('languageserver', {
            \   'ccls': {
            \     'command': s:ccls_path . '\ccls.exe',
            \     'filetypes': ['c', 'cc', 'cpp', 'c++', 'objc', 'objcpp'],
            \     'rootPatterns': ['.ccls', 'compile_commands.json', '.git/', '.hg/', '.root/'],
            \     'initializationOptions': {
            \         'cache': {
            \             'directory': $TEMP . '\ccls'
            \         },
            \         'highlight': {
            \             'lsRanges': v:true
            \         }
            \     }
            \   },
            \   'lua-language-server': {
            \       'cwd': s:lua_language_server_path,
            \       'command': s:lua_language_server_path . '\bin\Windows\lua-language-server.exe',
            \       'args': ['-E', '-e', 'LANG="zh-cn"', s:lua_language_server_path . '\main.lua'],
            \       'filetypes': ['lua']
            \   }
            \ })
call coc#config('session', { 'directory': s:session_path })
